﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;

namespace WmpRemoteServer
{
    class Server
    {
        //http://www.codeguru.com/csharp/csharp/cs_network/sockets/article.php/c7695
        private Socket serverSocket;
        private ArrayList clients;
        public delegate void NewCommandsEvent(string data);
        public event NewCommandsEvent commandEvent;

        public void StartServer(int port)
        {
            try
            {
                clients = new ArrayList();
                serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                IPEndPoint ipLocal = new IPEndPoint(IPAddress.Any, port);
                serverSocket.Bind(ipLocal);
                serverSocket.Listen(5);//Number of connections in queue
                serverSocket.BeginAccept(new AsyncCallback(OnClientConnect), null);
            }
            catch (SocketException se)
            {
                Console.Write("StartServer: " + se);//Port is likely in use.
            }
        }

        public void OnClientConnect(IAsyncResult asyn)
        {
            try
            {
                Socket clientSocket = serverSocket.EndAccept(asyn);
                clients.Add(clientSocket);
                commandEvent("playerInfo;get;<EOS>");
                commandEvent("playlist;get;<EOS>");
                WaitForData(clientSocket);
                serverSocket.BeginAccept(new AsyncCallback(OnClientConnect), null);
            }
            catch (Exception s)
            {
                Console.Write("OnClientConnect: " + s);
            }
        }

        public class StateObject
        {
            // Client  socket.
            public Socket workSocket = null;
            // Size of receive buffer.
            public const int BufferSize = 1024;
            // Receive buffer.
            public byte[] buffer = new byte[BufferSize];
            // Received data string.
            public StringBuilder sb = new StringBuilder();
        }

        public void WaitForData(Socket socket)
        {
            try
            {
                StateObject state = new StateObject();
                state.workSocket = socket;
                socket.BeginReceive(state.buffer, 0, StateObject.BufferSize, SocketFlags.None, new AsyncCallback(OnDataReceived), state);
            }
            catch (SocketException se)
            {
                Console.Write("WaitForData: " + se);
            }
        }

        public void OnDataReceived(IAsyncResult asyn)
        {
            Socket client = null;
            try
            {
                StateObject state = (StateObject)asyn.AsyncState;
                client = state.workSocket;

                int bytesRead = client.EndReceive(asyn);
                if (bytesRead > 0)
                {
                    state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead));
                    string content = state.sb.ToString();
                    if(content.Contains("<EOS>"))
                    {
                        Console.Write("Receive: " + content + "\n");
                        commandEvent(content);
                        WaitForData(state.workSocket);
                    }
                    else
                    {
                        client.BeginReceive(state.buffer, 0, StateObject.BufferSize, SocketFlags.None, new AsyncCallback(OnDataReceived), state);
                    }
                }
            }
            catch(SocketException se)
            {
                if (se.ErrorCode == 10054)//Client is closed
                {
                    if (client != null)
                        client.Close();
                    serverSocket.BeginAccept(new AsyncCallback(OnClientConnect), null);
                }
                else
                {
                    Console.Write("OnDataReceived: " + se);
                }
            }
        }

        public void sendString(string data)
        {
            try
            {
                byte[] byteData = Encoding.ASCII.GetBytes(data);
                Console.Write("Send: " + data + "\n");
                foreach (Socket s in clients)
                {
                    if (s != null)
                    {
                        if (s.Connected)
                        {
                            s.Send(byteData);
                        }
                    }
                }  
            }
            catch (SocketException se)
            {
                //No Client
                Console.Write("SendString: " + se);
                //clientSocket = null;
                //serverSocket.BeginAccept(new AsyncCallback(OnClientConnect), null);
            }
        }

        public void CloseSockets()
        {
            if (serverSocket != null)
                serverSocket.Close();
            foreach (Socket s in clients)
            {
                if (s != null)
                {
                    s.Close();
                }
            }

            
        }
    }
}
